/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.Charset;

import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.Protocol;

import com.cqcis.uip.base.framework.common.InterfaceLog;
import com.cqcis.uip.base.framework.utils.ByteBuffer;
import com.cqcis.uip.base.framework.utils.TimeUtil;

/**
 * 封装httpclient链接 HttpConnection, 执行日志过滤
 * 
 * @author huangmy
 * @date   2008-10-15
 */
public class HttpConnectionSupport extends HttpConnection {
	private static final byte[] CRLF = new byte[] {(byte) 13, (byte) 10};
    private HttpConnection connection; // HTTP 链接
    private ByteBuffer sendBuffer; // 发送数据
    private ByteBuffer recvBuffer; // 接收数据
    private InterfaceLog intfLog; // 日志
    
	public HttpConnectionSupport(HttpConnection connection, ByteBuffer sendBuf, 
			ByteBuffer recvBuf, InterfaceLog intfLog) {
		super(connection.getHost(), connection.getPort());
		
		if (connection == null || sendBuf == null || recvBuf == null || intfLog == null) {
			throw new NullPointerException("Connection can't be null.");
		}
		
		this.connection = connection;	
		this.sendBuffer = sendBuf;
		this.recvBuffer = recvBuf;
		this.intfLog = intfLog;
	}
	
	private void recordSendTime() {
		if (this.intfLog.getSendTime() == null) {
			this.intfLog.setSendTime(TimeUtil.getCurrentTime());
		}
	}
	
	private void recordRecvTime() {
		if (intfLog.getRecvTime() == null) {
			intfLog.setRecvTime(TimeUtil.getCurrentTime());
		}
	}
	
	private void recordSendData(byte[] data, int offset, int length) {
		try {
			this.sendBuffer.put(data, offset, length); // 保存
		} catch (Throwable e) {
		}
		
		recordSendTime();
	}
	
	private void recordSendData(byte[] data) {
		try {
			this.sendBuffer.put(data); // 保存
		} catch (Throwable e) {
		}
		
		recordSendTime();
	}
	
	private void recordSendData(String t) {
		try {
			this.sendBuffer.putString(t, Charset.defaultCharset().newEncoder()); // 保存
		} catch (Throwable e) {
		}
		
		recordSendTime();
	}
	
	private void recordSendData(String t, String charset) {
		try {
			this.sendBuffer.putString(t, Charset.forName(charset).newEncoder()); // 保存
		} catch (Throwable e) {
		}
		
		recordSendTime();
	}
	
	private void recordSendData(byte data) {
		try {
			this.sendBuffer.put(data); // 保存
		} catch (Throwable e) {
		}
		
		recordSendTime();
	}
    
	@Override
	public void close() {		
		this.connection.close();
	}
	
	@Override
	public boolean equals(Object obj) {
		return this.connection.equals(obj);
	}
	
	@Override
	public boolean closeIfStale() throws IOException {
		return this.connection.closeIfStale();
	}
	
	@Override
	public void flushRequestOutputStream() throws IOException {
		this.connection.flushRequestOutputStream();
	}
	
	@Override
	public String getHost() {
		return this.connection.getHost();
	}
	
	@Override
	public HttpConnectionManager getHttpConnectionManager() {
		return this.connection.getHttpConnectionManager();
	}
	
	@Override
	public InputStream getLastResponseInputStream() {
		return this.connection.getLastResponseInputStream();
	}
	
	@Override
	public InetAddress getLocalAddress() {
		return this.connection.getLocalAddress();
	}
	
	@Override
	public HttpConnectionParams getParams() {
		return this.connection.getParams();
	}
	
	@Override
	public int getPort() {
		return this.connection.getPort();
	}
	
	@Override
	public Protocol getProtocol() {
		return this.connection.getProtocol();
	}
	
	@Override
	public String getProxyHost() {
		return this.connection.getProxyHost();
	}
	
	@Override
	public int getProxyPort() {
		return this.connection.getProxyPort();
	}
	
	@Override
	public OutputStream getRequestOutputStream() throws IOException,
			IllegalStateException {		
		OutputStream os = this.connection.getRequestOutputStream();
		if (os == null) {
			return null;
		}
		return new OutputStreamLog(os);
	}
	
	private class OutputStreamLog extends OutputStream {
		private OutputStream os;
		
		public OutputStreamLog(OutputStream os) {
			this.os = os;
		}
		
		@Override
		public void close() throws IOException {
			this.os.close();
		}
		
		@Override
		public boolean equals(Object obj) {
			return this.os.equals(obj);
		}
		
		@Override
		public void flush() throws IOException {
			this.os.flush();
		}
		
		@Override
		public int hashCode() {
			return this.os.hashCode();
		}
		
		@Override
		public String toString() {
			return this.os.toString();
		}
		
		@Override
		public void write(int b) throws IOException {			
			recordSendData((byte)b);
			this.os.write(b);
		}
				
		@Override
		public void write(byte[] b) throws IOException {	
			recordSendData(b, 0, b.length);
			this.os.write(b);
		}
		
		@Override
		public void write(byte[] b, int off, int len) throws IOException {			
			recordSendData(b, off, len);
			this.os.write(b, off, len);
		}
	}
			
	@Override
	public InputStream getResponseInputStream() throws IOException,
			IllegalStateException {
		InputStream is = this.connection.getResponseInputStream();
		if (is == null) {
			return null;
		}
		return new InputStreamLog(is);
	}
	
	private class InputStreamLog extends InputStream {
		private InputStream is;
		
		public InputStreamLog(InputStream is) {
			this.is = is;
		}

		@Override
		public int read() throws IOException {
			int b = this.is.read();
			if (b < 0) {
				return b;
			}
			
			recordRecvTime();
			
			try {
				recvBuffer.put((byte)b);
			} catch (Throwable e) {
			}
			
			return b;
		}
		
		@Override
		public void close() throws IOException {
			this.is.close();
		}
		
		@Override
		public int available() throws IOException {
			return this.is.available();
		}
		
		@Override
		public boolean equals(Object obj) {
			return this.is.equals(obj);
		}
		
		@Override
		public int hashCode() {
			return this.is.hashCode();
		}
		
		@Override
		public synchronized void mark(int readlimit) {
			this.is.mark(readlimit);
		}
		
		@Override
		public boolean markSupported() {
			return this.is.markSupported();
		}
		
		@Override
		public int read(byte[] b) throws IOException {
			int len = this.is.read(b);
			
			try {				
				if (len > 0) {
				    recvBuffer.put(b, 0, len);
				}
			} catch (Throwable e) {
			}
			
			recordRecvTime();
			
			return len;
		}
		
		@Override
		public int read(byte[] b, int off, int len) throws IOException {			
			int count = this.is.read(b, off, len);
			try {
				if (count > 0) {
				    recvBuffer.put(b, off, count);
				}
			} catch (Throwable e) {
			}
			
			recordRecvTime();
			
			return count;
		}
		
		@Override
		public synchronized void reset() throws IOException {
			this.is.reset();
		}
		
		@Override
		public long skip(long n) throws IOException {
			return this.is.skip(n);
		}
		
		@Override
		public String toString() {
			return this.is.toString();
		}		
	}
	
	@Override
	public int getSendBufferSize() throws SocketException {
		return this.connection.getSendBufferSize();
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public int getSoTimeout() throws SocketException {
		return this.connection.getSoTimeout();
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public String getVirtualHost() {
		return this.connection.getVirtualHost();
	}
	
	@Override
	public int hashCode() {
		return this.connection.hashCode();
	}
	
	@Override
	public boolean isOpen() {
		return this.connection.isOpen();
	}
	
	@Override
	public boolean isProxied() {
		return this.connection.isProxied();
	}
	
	@Override
	public boolean isResponseAvailable() throws IOException {
		return this.connection.isResponseAvailable();
	}
	
	@Override
	public boolean isResponseAvailable(int timeout) throws IOException {
		return this.connection.isResponseAvailable(timeout);
	}
	
	@Override
	public boolean isSecure() {
		return this.connection.isSecure();
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public boolean isStaleCheckingEnabled() {
		return this.connection.isStaleCheckingEnabled();
	}
	
	@Override
	public boolean isTransparent() {
		return this.connection.isTransparent();
	}
	
	@Override
	public void open() throws IOException {
		this.connection.open();
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void print(String data) throws IOException, IllegalStateException {
		recordSendData(data);
		this.connection.print(data);
	}
	
	@Override
	public void print(String data, String charset) throws IOException,
			IllegalStateException {
		recordSendData(data, charset);
		this.connection.print(data, charset);
	}
	
	@Override
	public void printLine() throws IOException, IllegalStateException {
		recordSendData(CRLF);
		this.connection.printLine();
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void printLine(String data) throws IOException,
			IllegalStateException {
		recordSendData(data);
		this.connection.printLine(data);
	}
	
	@Override
	public void printLine(String data, String charset) throws IOException,
			IllegalStateException {
		recordSendData(data, charset);
		this.connection.printLine(data, charset);
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public String readLine() throws IOException, IllegalStateException {
		String t = this.connection.readLine();
		if (t != null) {
			recordRecvTime();
			
			try {
				this.recvBuffer.putString(t, Charset.defaultCharset()
						.newEncoder());
			} catch (Throwable e) {
			}
		}
		
		return t;
	}
	
	@Override
	public String readLine(String charset) throws IOException,
			IllegalStateException {
		String t = this.connection.readLine(charset);
		if (t != null) {
			recordRecvTime();
			
			try {
				this.recvBuffer.putString(t, Charset.forName(charset)
						.newEncoder());
			} catch (Throwable e) {
			}
		}
		
		return t;
	}
	
	@Override
	public void releaseConnection() {
		this.connection.releaseConnection();
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void setConnectionTimeout(int timeout) {
		this.connection.setConnectionTimeout(timeout);
	}
	
	@Override
	public void setHost(String host) throws IllegalStateException {
		this.connection.setHost(host);
	}
	
	@Override
	public void setHttpConnectionManager(
			HttpConnectionManager httpConnectionManager) {
		this.connection.setHttpConnectionManager(httpConnectionManager);
	}
	
	@Override
	public void setLastResponseInputStream(InputStream inStream) {
		this.connection.setLastResponseInputStream(inStream);
	}
	
	@Override
	public void setLocalAddress(InetAddress localAddress) {
		this.connection.setLocalAddress(localAddress);
	}
	
	@Override
	public void setParams(HttpConnectionParams params) {
		this.connection.setParams(params);
	}
	
	@Override
	public void setPort(int port) throws IllegalStateException {
		this.connection.setPort(port);
	}
	
	@Override
	public void setProtocol(Protocol protocol) {
		this.connection.setProtocol(protocol);
	}
	
	@Override
	public void setProxyHost(String host) throws IllegalStateException {
		this.connection.setProxyHost(host);
	}
	
	@Override
	public void setProxyPort(int port) throws IllegalStateException {
		this.connection.setProxyPort(port);
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void setSendBufferSize(int sendBufferSize) throws SocketException {
		this.connection.setSendBufferSize(sendBufferSize);
	}
	
	@Override
	public void setSocketTimeout(int timeout) throws SocketException,
			IllegalStateException {
		this.connection.setSocketTimeout(timeout);
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void setSoTimeout(int timeout) throws SocketException,
			IllegalStateException {
		this.connection.setSoTimeout(timeout);
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
		this.connection.setStaleCheckingEnabled(staleCheckEnabled);
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void setVirtualHost(String host) throws IllegalStateException {
		this.connection.setVirtualHost(host);
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void shutdownOutput() {
		this.connection.shutdownOutput();
	}
	
	@Override
	public String toString() {
		return this.connection.toString();
	}
	
	@Override
	public void tunnelCreated() throws IllegalStateException, IOException {
		this.connection.tunnelCreated();
	}
	
	@Override
	public void write(byte[] data) throws IOException, IllegalStateException {
		recordSendData(data, 0, data.length);
		this.connection.write(data);
	}
	
	@Override
	public void write(byte[] data, int offset, int length) throws IOException,
			IllegalStateException {
		recordSendData(data, offset, length);		
		this.connection.write(data, offset, length);
	}
	
	@Override
	public void writeLine() throws IOException, IllegalStateException {	
		recordSendData(CRLF);
		this.connection.writeLine();
	}
	
	@Override
	public void writeLine(byte[] data) throws IOException,
			IllegalStateException {
		recordSendData(data, 0, data.length);
		this.connection.writeLine(data);
	}
}
